#include <mcp/transport/transport_factory.h>
#include <mcp/transport/transport.h>
#include <mcp/error.h>
#include <iostream>
#include <sstream>
#include <memory>

namespace mcp {
namespace transport {

// TransportFactory implementation
struct TransportFactory::Impl {
    std::unordered_map<std::string, CreatorFunction> creators;
    std::unordered_map<std::string, ValidatorFunction> validators;
    mutable std::mutex mutex;

    Impl() {
        // Register built-in transport creators
        register_builtin_transports();
    }

    void register_builtin_transports() {
        // Register stdio transport
        creators["stdio"] = [](const TransportConfig& config) -> std::unique_ptr<Transport> {
            return std::make_unique<StdioTransport>();
        };

        validators["stdio"] = [](const TransportConfig& config) -> std::vector<std::string> {
            std::vector<std::string> errors;
            // Stdio transport has no required configuration
            return errors;
        };

        // Register HTTP transport
        creators["http"] = [](const TransportConfig& config) -> std::unique_ptr<Transport> {
            auto it = config.params.find("base_url");
            if (it == config.params.end() || !it->second.is_string()) {
                throw std::runtime_error("HTTP transport requires 'base_url' parameter");
            }
            return std::make_unique<HttpTransport>(it->second.as_string());
        };

        validators["http"] = [](const TransportConfig& config) -> std::vector<std::string> {
            std::vector<std::string> errors;
            auto it = config.params.find("base_url");
            if (it == config.params.end()) {
                errors.push_back("HTTP transport requires 'base_url' parameter");
            } else if (!it->second.is_string()) {
                errors.push_back("'base_url' parameter must be a string");
            } else if (it->second.as_string().empty()) {
                errors.push_back("'base_url' parameter cannot be empty");
            }
            return errors;
        };

        // Register WebSocket transport
        creators["websocket"] = [](const TransportConfig& config) -> std::unique_ptr<Transport> {
            auto it = config.params.find("url");
            if (it == config.params.end() || !it->second.is_string()) {
                throw std::runtime_error("WebSocket transport requires 'url' parameter");
            }
            return std::make_unique<WebSocketTransport>(it->second.as_string());
        };

        validators["websocket"] = [](const TransportConfig& config) -> std::vector<std::string> {
            std::vector<std::string> errors;
            auto it = config.params.find("url");
            if (it == config.params.end()) {
                errors.push_back("WebSocket transport requires 'url' parameter");
            } else if (!it->second.is_string()) {
                errors.push_back("'url' parameter must be a string");
            } else if (it->second.as_string().empty()) {
                errors.push_back("'url' parameter cannot be empty");
            } else if (it->second.as_string().substr(0, 4) != "ws://") {
                errors.push_back("'url' parameter must start with 'ws://' or 'wss://'");
            }
            return errors;
        };

        // Register SSE transport
        creators["sse"] = [](const TransportConfig& config) -> std::unique_ptr<Transport> {
            auto it = config.params.find("url");
            if (it == config.params.end() || !it->second.is_string()) {
                throw std::runtime_error("SSE transport requires 'url' parameter");
            }
            return std::make_unique<SseTransport>(it->second.as_string());
        };

        validators["sse"] = [](const TransportConfig& config) -> std::vector<std::string> {
            std::vector<std::string> errors;
            auto it = config.params.find("url");
            if (it == config.params.end()) {
                errors.push_back("SSE transport requires 'url' parameter");
            } else if (!it->second.is_string()) {
                errors.push_back("'url' parameter must be a string");
            } else if (it->second.as_string().empty()) {
                errors.push_back("'url' parameter cannot be empty");
            } else if (it->second.as_string().substr(0, 7) != "http://" &&
                      it->second.as_string().substr(0, 8) != "https://") {
                errors.push_back("'url' parameter must start with 'http://' or 'https://'");
            }
            return errors;
        };
    }
};

TransportFactory::TransportFactory()
    : impl_(std::make_unique<Impl>()) {}

TransportFactory::~TransportFactory() = default;

TransportFactory& TransportFactory::instance() {
    static TransportFactory instance;
    return instance;
}

std::unique_ptr<Transport> TransportFactory::create_transport(const TransportConfig& config) {
    std::lock_guard<std::mutex> lock(impl_->mutex);

    auto it = impl_->creators.find(config.type);
    if (it == impl_->creators.end()) {
        throw std::runtime_error("Unknown transport type: " + config.type);
    }

    return it->second(config);
}

std::vector<std::string> TransportFactory::validate_config(const TransportConfig& config) const {
    std::lock_guard<std::mutex> lock(impl_->mutex);

    std::vector<std::string> errors;

    // Check if transport type is supported
    if (impl_->creators.find(config.type) == impl_->creators.end()) {
        errors.push_back("Unsupported transport type: " + config.type);
        return errors;
    }

    // Validate specific configuration
    auto validator_it = impl_->validators.find(config.type);
    if (validator_it != impl_->validators.end()) {
        auto validation_errors = validator_it->second(config);
        errors.insert(errors.end(), validation_errors.begin(), validation_errors.end());
    }

    return errors;
}

std::vector<std::string> TransportFactory::get_supported_types() const {
    std::lock_guard<std::mutex> lock(impl_->mutex);

    std::vector<std::string> types;
    for (const auto& [type, _] : impl_->creators) {
        types.push_back(type);
    }

    std::sort(types.begin(), types.end());
    return types;
}

bool TransportFactory::is_supported(const std::string& type) const {
    std::lock_guard<std::mutex> lock(impl_->mutex);
    return impl_->creators.find(type) != impl_->creators.end();
}

void TransportFactory::register_transport(const std::string& type,
                                         CreatorFunction creator,
                                         ValidatorFunction validator) {
    std::lock_guard<std::mutex> lock(impl_->mutex);
    impl_->creators[type] = std::move(creator);
    impl_->validators[type] = std::move(validator);
}

void TransportFactory::unregister_transport(const std::string& type) {
    std::lock_guard<std::mutex> lock(impl_->mutex);
    impl_->creators.erase(type);
    impl_->validators.erase(type);
}

// TransportConfig implementation
TransportConfig::TransportConfig(std::string type, json::Value params)
    : type(std::move(type)), params(std::move(params)) {}

TransportConfig TransportConfig::create_stdio() {
    return TransportConfig("stdio", json::Value::object({}));
}

TransportConfig TransportConfig::create_http(const std::string& base_url,
                                             const std::optional<int>& timeout_ms,
                                             const std::optional<std::string>& auth_token) {
    json::Value params = json::Value::object({
        {"base_url", base_url}
    });

    if (timeout_ms) {
        params["timeout_ms"] = *timeout_ms;
    }

    if (auth_token) {
        params["auth_token"] = *auth_token;
    }

    return TransportConfig("http", std::move(params));
}

TransportConfig TransportConfig::create_websocket(const std::string& url,
                                                   const std::optional<int>& timeout_ms,
                                                   const std::optional<std::vector<std::string>>& protocols) {
    json::Value params = json::Value::object({
        {"url", url}
    });

    if (timeout_ms) {
        params["timeout_ms"] = *timeout_ms;
    }

    if (protocols) {
        params["protocols"] = json::Value::array();
        for (const auto& protocol : *protocols) {
            params["protocols"].push_back(protocol);
        }
    }

    return TransportConfig("websocket", std::move(params));
}

TransportConfig TransportConfig::create_sse(const std::string& url,
                                           const std::optional<int>& timeout_ms,
                                           const std::optional<std::string>& last_event_id) {
    json::Value params = json::Value::object({
        {"url", url}
    });

    if (timeout_ms) {
        params["timeout_ms"] = *timeout_ms;
    }

    if (last_event_id) {
        params["last_event_id"] = *last_event_id;
    }

    return TransportConfig("sse", std::move(params));
}

std::optional<TransportConfig> TransportConfig::from_json(const json::Value& json) {
    if (!json.is_object()) {
        return std::nullopt;
    }

    try {
        auto type = json.get("type", "").as_string();
        if (type.empty()) {
            return std::nullopt;
        }

        auto params = json.get("params", json::Value::object({}));
        return TransportConfig(std::move(type), std::move(params));
    } catch (...) {
        return std::nullopt;
    }
}

json::Value TransportConfig::to_json() const {
    return json::Value::object({
        {"type", type},
        {"params", params}
    });
}

// TransportInfo implementation
TransportInfo::TransportInfo(std::string name,
                             std::string description,
                             std::vector<std::string> required_params,
                             std::vector<std::string> optional_params,
                             std::vector<TransportExample> examples)
    : name(std::move(name)),
      description(std::move(description)),
      required_params(std::move(required_params)),
      optional_params(std::move(optional_params)),
      examples(std::move(examples)) {}

std::vector<TransportInfo> TransportFactory::get_transport_info() const {
    std::lock_guard<std::mutex> lock(impl_->mutex);

    std::vector<TransportInfo> info;

    // Stdio transport info
    info.push_back(TransportInfo{
        .name = "stdio",
        .description = "Standard input/output transport for local communication",
        .required_params = {},
        .optional_params = {},
        .examples = {
            TransportExample{
                .name = "Basic stdio",
                .config = TransportConfig::create_stdio(),
                .description = "Creates a stdio transport for local communication"
            }
        }
    });

    // HTTP transport info
    info.push_back(TransportInfo{
        .name = "http",
        .description = "HTTP-based transport for web communication",
        .required_params = {"base_url"},
        .optional_params = {"timeout_ms", "auth_token"},
        .examples = {
            TransportExample{
                .name = "Basic HTTP",
                .config = TransportConfig::create_http("http://localhost:8080"),
                .description = "Creates an HTTP transport connecting to localhost:8080"
            },
            TransportExample{
                .name = "HTTP with timeout",
                .config = TransportConfig::create_http("https://api.example.com", 5000),
                .description = "Creates an HTTP transport with 5 second timeout"
            }
        }
    });

    // WebSocket transport info
    info.push_back(TransportInfo{
        .name = "websocket",
        .description = "WebSocket transport for bidirectional real-time communication",
        .required_params = {"url"},
        .optional_params = {"timeout_ms", "protocols"},
        .examples = {
            TransportExample{
                .name = "Basic WebSocket",
                .config = TransportConfig::create_websocket("ws://localhost:8080/ws"),
                .description = "Creates a WebSocket transport connecting to localhost:8080"
            },
            TransportExample{
                .name = "Secure WebSocket",
                .config = TransportConfig::create_websocket("wss://api.example.com/ws", 10000),
                .description = "Creates a secure WebSocket transport with 10 second timeout"
            }
        }
    });

    // SSE transport info
    info.push_back(TransportInfo{
        .name = "sse",
        .description = "Server-Sent Events transport for unidirectional real-time updates",
        .required_params = {"url"},
        .optional_params = {"timeout_ms", "last_event_id"},
        .examples = {
            TransportExample{
                .name = "Basic SSE",
                .config = TransportConfig::create_sse("http://localhost:8080/events"),
                .description = "Creates an SSE transport connecting to localhost:8080"
            },
            TransportExample{
                .name = "Secure SSE",
                .config = TransportConfig::create_sse("https://api.example.com/events", 5000),
                .description = "Creates a secure SSE transport with 5 second timeout"
            }
        }
    });

    return info;
}

// Utility functions
std::string format_validation_errors(const std::vector<std::string>& errors) {
    if (errors.empty()) {
        return "Configuration is valid";
    }

    std::stringstream ss;
    ss << "Configuration validation errors:\n";
    for (const auto& error : errors) {
        ss << "  - " << error << "\n";
    }

    return ss.str();
}

TransportConfig create_transport_config_from_url(const std::string& url) {
    if (url.substr(0, 7) == "stdio://") {
        return TransportConfig::create_stdio();
    } else if (url.substr(0, 7) == "http://" || url.substr(0, 8) == "https://") {
        return TransportConfig::create_http(url);
    } else if (url.substr(0, 5) == "ws://" || url.substr(0, 6) == "wss://") {
        return TransportConfig::create_websocket(url);
    } else if (url.substr(0, 6) == "sse://") {
        return TransportConfig::create_sse(url.substr(6)); // Remove sse:// prefix
    } else {
        throw std::runtime_error("Cannot determine transport type from URL: " + url);
    }
}

std::string guess_transport_type(const std::string& endpoint) {
    if (endpoint == "stdio" || endpoint == "-" || endpoint.empty()) {
        return "stdio";
    } else if (endpoint.substr(0, 7) == "http://" || endpoint.substr(0, 8) == "https://") {
        return "http";
    } else if (endpoint.substr(0, 5) == "ws://" || endpoint.substr(0, 6) == "wss://") {
        return "websocket";
    } else if (endpoint.substr(0, 6) == "sse://") {
        return "sse";
    } else {
        // Default to stdio for local endpoints
        return "stdio";
    }
}

} // namespace transport
} // namespace mcp